package com.runner.screen;

import java.util.ArrayList;
import java.util.Iterator;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter;
import com.badlogic.gdx.input.GestureDetector;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.runner.RedRunner;
import com.runner.game.GameHandler;
import com.runner.input.CollisionHandler;
import com.runner.input.InputHandler;
import com.runner.observer.Event;
import com.runner.observer.Observer;
import com.runner.observer.Subject;
import com.runner.sound.AudioManager;
import com.runner.utility.Constants;
import com.runner.utility.Locator;

public class GameScreen implements Screen, Constants, Observer {

	private Box2DDebugRenderer renderer;
	private OrthographicCamera camera;
	private BitmapFont smallFont, tiny, tutorial;
	private SpriteBatch gameSprites;
	private SpriteBatch uiBatch;
	private Stage stage;
	private final ArrayList<TransitionEffect> effects;
	private boolean pause;
	private Subject subject;
	private PauseScreen pauseScreen;
	private BackgroundManager background;
	private AudioManager audioManager;
	private GameHandler gameHandler;
	private ImageButton pauseButton;
	private boolean restart;
	private boolean started;
	private float restartTimer;
	private boolean showDeath;
	private DeathScreen deathScreen;
	private RedRunner game;
	private boolean coinMultiplier;
	private boolean newTopDistance;
	private boolean loadout = false;
	public static final float COUNTDOWN = 3;

	public GameScreen(OrthographicCamera camera, AudioManager audioManager, Subject subject, RedRunner game) {
		this.camera = camera;
		this.audioManager = audioManager;
		this.subject = subject;
		this.game = game;
		restart = true;
		started = false;
		restartTimer = COUNTDOWN;

		uiBatch = new SpriteBatch();
		gameSprites = new SpriteBatch();

		subject.addObserver(this);
		subject.addObserver(Locator.getAchvs());

		pause = false;

		effects = new ArrayList<TransitionEffect>();

		// table and stage ui
		createUI();
		pauseScreen = new PauseScreen(game, this, subject);

		// game
		resize((int)ACTUAL_VIRTUAL_WIDTH, (int)ACTUAL_VIRTUAL_HEIGHT);
		

		// fonts
		FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal(FONT));
		FreeTypeFontParameter param = new FreeTypeFontParameter();
		param.size = (int) (80 );
		smallFont = generator.generateFont(param);
		smallFont.setColor(Color.DARK_GRAY);

		param.size = (int) (48 );
		tutorial = generator.generateFont(param);
		tutorial.setColor(Color.DARK_GRAY);

		param.size = (int) (54 );
		tiny = generator.generateFont(param);
		tiny.setColor(Color.DARK_GRAY);
		generator.dispose();

		gameHandler = new GameHandler(subject, tutorial);
		deathScreen = new DeathScreen(this, gameHandler, tiny, subject);

		renderer = new Box2DDebugRenderer();
		CollisionHandler ch = new CollisionHandler();
		Locator.getWorld().setContactListener(ch);
		background = new BackgroundManager();
		// input
		GestureDetector gd = new GestureDetector(new InputHandler(this));
		Locator.getInputMultiplexer().addProcessor(gd);
		Locator.getInputMultiplexer().addProcessor(stage);
		
		uiBatch.setTransformMatrix(stage.getCamera().view);
		uiBatch.setProjectionMatrix(stage.getCamera().projection);
	}

	/**
	 * Renders background, then game sprites, then coin image, then energy
	 * bubbles, pause button then text
	 */
	@Override
	public void render(float delta) {
		// Entire game speed will affect everything
		delta = delta * Locator.getOverallGameSpeed();

		camera.update();
		Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		uiBatch.begin();
		background.render(uiBatch);
		uiBatch.end();
		// renderer.render(Locator.getWorld(), camera.combined);
		gameSprites.setProjectionMatrix(camera.combined);

		// render all game sprites
		gameSprites.begin();
		gameHandler.render(gameSprites);
		gameSprites.end();

		uiBatch.begin();
		// renders text at top of screen
		renderGUI(delta);
		uiBatch.end();

		// pause game if player died
		if (!showDeath) {
			// pause game if pause is touched
			if (!pause) {
				renderCountdown(delta);
				// dont update until countdown is finished
				if (!restart) {
					update(delta);
					Locator.getWorld().step(1 / 30f, 6, 2);
				}
				stage.act(delta);
				stage.draw();
			} else {
				pauseScreen.render(delta);
			}
		}

		if (showDeath) {
			deathScreen.render(uiBatch, delta);
		}

		Locator.getEvents().update(delta);
	}

	/**
	 * Update all entities, music, background
	 * 
	 * @param delta
	 */
	public void update(float delta) {
		// update game entities
		background.update(delta);
		gameHandler.update(delta);
		if (!audioManager.isPlayingGameMusic() && !audioManager.isSwitchingMusic()) {
			subject.notify(Event.EVENT_MUSIC_FINISHED);
		}
		uiBatch.begin();
		updateParticles(delta);
		uiBatch.end();
	}

	public void loadLevel(PLAYER_TYPE pType, TRACKTYPE tType, ArrayList<BOOST_TYPE> bType) {
		gameHandler.loadLevel(pType, tType, bType);
	}

	/**
	 * Render text at top of screen count
	 * 
	 * @param delta
	 */
	private void renderGUI(float delta) {
		 //smallFont.draw(uiBatch, "" + Gdx.graphics.getFramesPerSecond(),
		 //ACTUAL_VIRTUAL_WIDTH * 0.05f, ACTUAL_VIRTUAL_HEIGHT * 0.9f);
		// fontSmall.draw(uiBatch, "Top: " + Locator.getTopDistance(),
		// ACTUAL_VIRTUAL_WIDTH * 0.70f, ACTUAL_VIRTUAL_HEIGHT * 0.99f);
	}

	/**
	 * Renders the "Ready, Run!" text when the game is restarting
	 * 
	 * @param delta
	 */
	public void renderCountdown(float delta) {
		// provide a countdown every time game starts
		if (restart || !started) {
			restartTimer -= delta;
			uiBatch.begin();
			if (restartTimer >= 2f) {
				smallFont.draw(uiBatch, "Ready", ACTUAL_VIRTUAL_WIDTH * 0.4f, ACTUAL_VIRTUAL_HEIGHT * 0.54f);
			} else if (restartTimer >= 1.3f) {
				smallFont.draw(uiBatch, "Set", ACTUAL_VIRTUAL_WIDTH * 0.43f, ACTUAL_VIRTUAL_HEIGHT * 0.54f);
			} else if (restartTimer > 0.3f) {
				smallFont.draw(uiBatch, "Run!", ACTUAL_VIRTUAL_WIDTH * 0.42f, ACTUAL_VIRTUAL_HEIGHT * 0.54f);
				restart = false;
			} else {
				started = true;
				restartTimer = COUNTDOWN;
			}
			uiBatch.end();
		}
	}

	/**
	 * update particles from the game
	 * 
	 * @param delta
	 */
	public void updateParticles(float delta) {
		for (Iterator<ParticleEffect> i = Locator.getEffects().iterator(); i.hasNext();) {
			ParticleEffect pe = i.next();
			pe.draw(uiBatch, delta);
			if (pe.isComplete()) {
				Locator.getEffects().remove(i);
			}
		}
	}

	/**
	 * restarts the level
	 */
	public void restart() {
		gameHandler.reset();
		background.reset();
		started = false;
		restart = true;
		coinMultiplier = false;
		restartTimer = COUNTDOWN;
		showDeath = false;
		Locator.getEffects().clear();
	}

	/**
	 * Called when screen is resized
	 */
	@Override
	public void resize(int width, int height) {
		float fWidth = width;
		float fHeight = height;
		fWidth *= SCALE;
		fHeight *= SCALE;
		// calculate new viewport
		//calcViewport(fWidth, fHeight);
		Locator.getViewport().update(width, height);
		// sprite scales used for proportions
		setSpriteScales(1,1);

		pauseScreen.resize(width, height);
	}

	/**
	 * Bases the resolution off a virtual resolution and crops the screen based
	 * on that
	 * 
	 * @param fWidth
	 * @param fHeight
	 */
	public void calcViewport(float fWidth, float fHeight) {
		float aspectRatio = (float) fWidth / (float) fHeight;
		float scale = 1f;
		Vector2 crop = new Vector2(0f, 0f);
		if (aspectRatio > ASPECT_RATIO) {
			scale = (float) fHeight / (float) VIRTUAL_HEIGHT;
			crop.x = (fWidth - VIRTUAL_WIDTH * scale) / 2f;
		} else if (aspectRatio < ASPECT_RATIO) {
			scale = (float) fWidth / (float) VIRTUAL_WIDTH;
			crop.y = (fHeight - VIRTUAL_HEIGHT * scale) / 2f;
		} else {
			scale = (float) fWidth / (float) VIRTUAL_WIDTH;
		}
		float w = (float) VIRTUAL_WIDTH * scale;
		float h = (float) VIRTUAL_HEIGHT * scale;
		//viewport = new ExtendViewport(crop.x, crop.y, w, h);
	}

	/**
	 * IF PORTRAIT, THIS IS CORRECT...IF LANDSCAPE, SWITCH Y_SCALE AND X_SCALE
	 * 
	 * @param x_scale
	 * @param y_scale
	 * @param view
	 */
	public void setSpriteScales(float x_scale, float y_scale) {
		// swap values
		Locator.setSpriteScale(x_scale, y_scale);
	}

	/**
	 * Called when the game screen appears
	 */
	@Override
	public void show() {

		// reset fade effects
		effects.clear();
		effects.add(new FadeOutTransitionEffect(0.5f));
		effects.add(new FadeInTransitionEffect(0.5f));
		Gdx.input.setInputProcessor(Locator.getInputMultiplexer());
		resumeGame();

	}

	/**
	 * Called when game screen goes away
	 */
	@Override
	public void hide() {
		if (!showDeath) {
			Locator.writeSave();
			Gdx.input.setInputProcessor(null);
			pause();
		}
	}

	@Override
	public void pause() {
		if (!showDeath) {
			Locator.getAudio().stopMusic();
			Locator.writeSave();
			pause = true;
			pauseScreen.pause();
			restart = true;
			started = false;
			restartTimer = COUNTDOWN;
		}
	}

	@Override
	public void resume() {
	}

	public void resumeGame() {
		pauseScreen.resume();
		pause = false;
	}

	@Override
	public void dispose() {

	}

	/**
	 * Set pause button for game screen
	 */
	private void createPauseButton() {
		Image image = new Image(new Texture(Gdx.files.internal("data/art/pause.png")));
		pauseButton = new ImageButton(image.getDrawable());
		pauseButton.addListener(new ClickListener() {
			@Override
			public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
				subject.notify(Event.EVENT_CLICK);
				pause();
				return false;
			}
		});
	}

	/**
	 * Creates the coin image and table with the stage for the pause button
	 */
	public void createUI() {
		createPauseButton();
		stage = new Stage(Locator.getViewport());
		pauseButton.setPosition(ACTUAL_VIRTUAL_WIDTH * 0.85f, ACTUAL_VIRTUAL_HEIGHT * 0.85f);
		stage.addActor(pauseButton);
	}

	/**
	 * This class is an observer so onNotify handles the events
	 */
	@Override
	public void onNotify(Event event) {
		System.out.println(event.name());
		switch (event) {
		case EVENT_UNPAUSE:
			show();
			break;
		case EVENT_MENU_TO_GAME:

			break;
		case EVENT_SHOW_WIN_RESULTS:
			results(false);
			break;
		case EVENT_SHOW_RESULTS:
			results(true);
			break;
		case EVENT_COIN_MULT:
			coinMultiplier = true;
			break;
		case EVENT_PLAYER_PICKED:
			updateSprites();
			break;
		case EVENT_RINGER_TUTORIAL_COMPLETE:
			Locator.getUnlocks().setRingerTutorial(true);
			Locator.writeSave();
			break;
		case EVENT_APRIL_TUTORIAL_COMPLETE:
			Locator.getUnlocks().setAprilTutorial(true);
			Locator.writeSave();
			break;
		case EVENT_GEO_TUTORIAL_COMPLETE:
			Locator.getUnlocks().setGeoTutorial(true);
			Locator.writeSave();
			break;
		case EVENT_NEW_TOP_DISTANCE:
			newTopDistance = true;
			break;
		default:
			break;
		}
	}

	private void updateSprites() {
		background.updateBackground(gameHandler.getpType());

	}

	private void results(boolean death) {
		Gdx.input.setInputProcessor(null);
		deathScreen.show(death, newTopDistance);
		showDeath = true;
		newTopDistance = false;
	}

	public void finishDeathScreen(boolean restart) {
		Locator.increaseAdCounter();
		if (Locator.getAdCounter() >= PLAYS_BEFORE_AD) {
			Locator.resetAdCounter();
			Locator.getDevice().displayInterstitial();
		}
		if (restart) {
			Gdx.input.setInputProcessor(Locator.getInputMultiplexer());
			restart();
			loadSameLevel();
			loadout = false;
		} else {
			restart();
			gameToMenu();
			loadout = true;
		}

	}

	private void loadSameLevel() {
		gameHandler.loadSameLevel();

	}

	private void gameToMenu() {
		Locator.getGame().setScreen(game.getMenuScreen());
		subject.notify(Event.EVENT_GAME_TO_MENU);
		game.getMenuScreen().reset();
		game.getMenuScreen().switchToParamScreen();

	}
	
	public void resumeFromAd(){
		if (!loadout) {
			show();
		}
	}

	public boolean touchDown(float x, float y, boolean b) {
		gameHandler.touchDown(x, y, b);
		return false;
	}

	public boolean fling(float velocityX, float velocityY) {
		gameHandler.fling(velocityX, velocityY);
		return false;
	}

	public boolean tap(float x, float y) {
		gameHandler.tap(x, y);
		return false;
	}

	public boolean isPause() {
		return pause;
	}

	public void setPause(boolean pause) {
		this.pause = pause;
	}

	public boolean isRestart() {
		return restart;
	}

	public Subject getSubject() {
		return subject;
	}

	public Box2DDebugRenderer getRenderer() {
		return renderer;
	}

	public boolean isCoinMultiplier() {
		return coinMultiplier;
	}

	public AudioManager getAudioManager() {
		return audioManager;
	}

}
